#include <stdio.h>  // for puts
#include "Parser.h"
#include "Node.h"
#include "../Scanner-Automat/Scanner.h"

#define firstStatement (checkTT(IDENTIFIER) || checkTT(PRINT_TYPE) \
						|| checkTT(READ_TYPE) || checkTT(SIGN_OPENBRACE) \
						|| checkTT(IF_TYPE) || checkTT(WHILE_TYPE))

#define firstOp (checkTT(SIGN_PLUS) || checkTT(SIGN_MINUS) || checkTT(SIGN_MULTI) \
						|| checkTT(SIGN_DIV) || checkTT(SIGN_SMALLER)	\
						|| checkTT(SIGN_GREATER) || checkTT(SIGN_ASSIGN)	\
						|| checkTT(SIGN_EQUAL) || checkTT(SIGN_AND))

Parser::Parser(Scanner &NewScanner) :
		scanner(NewScanner) {
}

Parser::~Parser() {
	delete root;
}

inline bool Parser::nextToken() {
	return scanner.nextToken(currentToken);
}

bool Parser::checkTT(TokenType type) {
	return (currentToken.getType() == type);
}

void Parser::parse() {
	nextToken();
	root = prog();
}

Node* Parser::prog() {
	puts("prog!");
	Node* progPtr = new Node(PROG, NULL);
	progPtr->addNode(decls());
	progPtr->addNode(statements());
	return progPtr;
}

Node* Parser::decls() {
	puts("delcs!");
	Node* declsPtr = 0;
	if (checkTT(INT_TYPE)) {
		declsPtr = new Node(DECLS, NULL);
		declsPtr->addNode(decl());
		if (checkTT(SIGN_SEMICOLON)) {
			nextToken();
		} else {
			error();
		}
		declsPtr->addNode(decls());
	} else if (firstStatement || checkTT(END_OF_FILE)) {
		puts("No more Decl!");
		// decl is empty (epsilon) -> leave decls
	} else {
		error();
	}
	return declsPtr;
}

Node* Parser::decl() {
	puts("delc!");
	Node* declPtr = 0;
	if (checkTT(INT_TYPE)) {
		declPtr = new Node(DECL, NULL);
		Node* intPtr = new Node(TERM, new Token(currentToken));
		declPtr->addNode(intPtr);
		nextToken();
		// check if we have an array or just a normal variable
		declPtr->addNode(array());
		if (checkTT(IDENTIFIER)) {
			puts("Identifier!");
			declPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
	} else {
		error();
	}
	return declPtr;
}

Node* Parser::array() {
	puts("array!");
	Node* arrayPtr = 0;
	if (checkTT(SIGN_OPENBRACKET)) {
		arrayPtr = new Node(ARRAY, NULL);
		arrayPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		if (checkTT(INTEGER)) {
			arrayPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		if (checkTT(SIGN_CLOSEBRACKET)) {
			arrayPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
	} else if (checkTT(IDENTIFIER)) {
	} else {
		error();
	}
	return arrayPtr;
}

Node* Parser::statement() {
	puts("statement!");
	Node* statementPtr = 0;
	if (checkTT(IDENTIFIER)) {
		puts("Identifier!");
		statementPtr = new Node(STATEMENT, NULL);
		statementPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		statementPtr->addNode(index());
		if (checkTT(SIGN_ASSIGN)) {
			puts("Assign!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(exp());
	} else if (checkTT(PRINT_TYPE)) {
		puts("print type!");
		statementPtr = new Node(STATEMENT, NULL);
		statementPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		if (checkTT(SIGN_OPENPAREN)) {
			puts("openparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(exp());
		if (checkTT(SIGN_CLOSEPAREN)) {
			puts("closeparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
	} else if (checkTT(READ_TYPE)) {
		puts("readtype!");
		statementPtr = new Node(STATEMENT, NULL);
		statementPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		if (checkTT(SIGN_OPENPAREN)) {
			puts("openparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		if (checkTT(IDENTIFIER)) {
			puts("Identifier!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(index());
		if (checkTT(SIGN_CLOSEPAREN)) {
			puts("closeparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
	} else if (checkTT(SIGN_OPENBRACE)) {
		puts("openbrace!");
		statementPtr = new Node(STATEMENT, NULL);
		statementPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		statementPtr->addNode(statements());
		if (checkTT(SIGN_CLOSEBRACE)) {
			puts("closebrace!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
	} else if (checkTT(IF_TYPE)) {
		puts("iftype!");
		statementPtr = new Node(STATEMENT, NULL);
		statementPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		if (checkTT(SIGN_OPENPAREN)) {
			puts("openparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(exp());
		if (checkTT(SIGN_CLOSEPAREN)) {
			puts("closeparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(statement());
		if (checkTT(ELSE_TYPE)) {
			puts("elsetype!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(statement());
	} else if (checkTT(WHILE_TYPE)) {
		puts("whiletype!");
		statementPtr = new Node(STATEMENT, NULL);
		statementPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		if (checkTT(SIGN_OPENPAREN)) {
			puts("openparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(exp());
		if (checkTT(SIGN_CLOSEPAREN)) {
			puts("closeparen!");
			statementPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
		statementPtr->addNode(statement());
	} else {
		error();
	}
	return statementPtr;
}

Node* Parser::statements() {
	puts("statements!");
	Node* statementsPtr = 0;
	if (firstStatement) {
		statementsPtr = new Node(STATEMENTS, NULL);
		statementsPtr->addNode(statement());
		if (checkTT(SIGN_SEMICOLON)) {
			puts("semicolon!");
			nextToken();
		} else {
			error();
		}
		statementsPtr->addNode(statements());
	} else if (checkTT(END_OF_FILE) || checkTT(SIGN_CLOSEBRACE)) {
		puts("END_OF_FILE|closebrace!");
	} else {
		error();
	}
	return statementsPtr;
}

Node* Parser::index() {
	puts("index!");
	Node* indexPtr = 0;
	if (checkTT(SIGN_OPENBRACKET)) {
		puts("openbracket!");
		indexPtr = new Node(INDEX, NULL);
		indexPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		indexPtr->addNode(exp());
		if (checkTT(SIGN_CLOSEBRACKET)) {
			puts("closebracket!");
			indexPtr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
	} else if (checkTT(SIGN_ASSIGN) || checkTT(SIGN_CLOSEPAREN) || firstOp) {
	} else {
		error();
	}
	return indexPtr;
}

Node* Parser::exp() {
	puts("exp!");
	Node* expPtr = new Node(EXP, NULL);
	expPtr->addNode(exp2());
	expPtr->addNode(op_exp());
	return expPtr;
}

Node* Parser::exp2() {
	puts("exp2!");
	Node* exp2Ptr = 0;
	if (checkTT(SIGN_OPENPAREN)) {
		puts("openparen");
		exp2Ptr = new Node(EXP2, NULL);
		exp2Ptr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		exp2Ptr->addNode(exp());
		if (checkTT(SIGN_CLOSEPAREN)) {
			puts("closeparen");
			exp2Ptr->addNode(new Node(TERM, new Token(currentToken)));
			nextToken();
		} else {
			error();
		}
	} else if (checkTT(IDENTIFIER)) {
		puts("identifier");
		exp2Ptr = new Node(EXP2, NULL);
		exp2Ptr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		exp2Ptr->addNode(index());
	} else if (checkTT(INTEGER)) {
		puts("integer");
		exp2Ptr = new Node(EXP2, NULL);
		exp2Ptr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
	} else if (checkTT(SIGN_MINUS)) {
		puts("minus");
		exp2Ptr = new Node(EXP2, NULL);
		exp2Ptr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		exp2Ptr->addNode(exp2());
	} else if (checkTT(SIGN_NOT)) {
		puts("not");
		exp2Ptr = new Node(EXP2, NULL);
		exp2Ptr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
		exp2Ptr->addNode(exp2());
	} else {
		error();
	}
	return exp2Ptr;
}

Node* Parser::op() {
	Node* opPtr = 0;
	if (firstOp) {
		puts("op!");
		opPtr = new Node(OP, NULL);
		opPtr->addNode(new Node(TERM, new Token(currentToken)));
		nextToken();
	} else {
		error();
	}
	return opPtr;
}

Node* Parser::op_exp() {
	puts("op_exp!");
	Node* op_expPtr = 0;
	if (firstOp) {
		op_expPtr = new Node(OP_EXP, NULL);
		op_expPtr->addNode(op());
		op_expPtr->addNode(exp());
	} else if (checkTT(SIGN_CLOSEPAREN) || checkTT(SIGN_CLOSEBRACKET)
			|| checkTT(SIGN_SEMICOLON) || checkTT(ELSE_TYPE)) {
	} else {
		error();
	}
	return op_expPtr;
}

void Parser::error() {
	puts("Invalid Token sequence!");
}
